//#include <bits/stl_alloc.h>
#include <stdexcept>
#include <string>
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
#include <sstream>
#include <fstream>
#include <deque>
#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/regex.hpp>

//# define DEBUG_LEVEL 9
#include <debug_func.h>
#include <perlutil.h>
#include <ultoa.h>
#include <bl2seq_mine.h>
#include <portable_timer.h>
#include <progress_indicator.h>
#include <bcpp_parsefasta.h>
#include <bcpp_read_seq.h>
#include <stlhelper.h>
#include <string_hash.h>
#include <print_error.h>
#include <ext/hash_map>
#include <gc.h>

#include "get_arg.h"

using BioCPP::t_collect_sequences;
using BioCPP::t_collect_sequences_report_errors_to_strm;
using BioCPP::eSEQ_NODUPLICATES;
using BioCPP::eSEQ_DUPLICATES;
using std::cerr;
using std::cout;
using std::string;
using std::vector;
using std::deque;
using std::istream;
using std::ifstream;
using std::sort;
using std::make_pair;
using std::pair;
using std::ostringstream;
using boost::lexical_cast;
using boost::tie;



//________________________________________________________________________________________

//	t_collect_accessions_from_strm

//________________________________________________________________________________________
using BioCPP::t_collect_sequences_report_errors;
struct t_filter_sequences_by_regex
{
	unsigned							cnt_filtered;
	unsigned							cnt_sequences;
	std::string							seqid_regex_str;
	t_progress_indicator&				dots;
	t_collect_sequences_report_errors& 	error_report;
	unsigned							acc_beg_line;	// accession begin line for error

	t_filter_sequences_by_regex(  	const std::string&				seqid_regex_str_,
									t_progress_indicator&			dots_,
									t_collect_sequences_report_errors& error_report_)
			:
			cnt_filtered(0),
			cnt_sequences(0),
			seqid_regex_str(seqid_regex_str_),
			dots(dots_),
			error_report(error_report_),
			acc_beg_line(0)
		 {	}



	// callback
	bool operator()(std::string& accession, std::string& sequence,
						unsigned acc_filepos,
						unsigned seq_filepos);
};



bool t_filter_sequences_by_regex::operator()(std::string& accession, std::string& sequence,
									unsigned /*acc_filepos*/, unsigned /*seq_filepos*/)
{
	// print progress dots
	++dots;
	++cnt_sequences;

	// get line counts for error messages
	unsigned curr_acc_beg_line = acc_beg_line;
	acc_beg_line += std::count(sequence.begin(), sequence.end(), '\n') + 1;

	// bad sequence!!!
	if (!sequence.length())
	{
		error_report.zero_length_sequence(curr_acc_beg_line);
		return true;
	}
	if (!accession.length())
	{
		error_report.zero_length_accession(curr_acc_beg_line);
		return true;
	}

	// match id
	{
		static boost::regex seqid_regex(seqid_regex_str);
		boost::match_results<std::string::iterator> id_match;
		if (boost::regex_search(accession.begin(), accession.end(), id_match, seqid_regex))
		{
			cout << ">" << accession <<  "\n" << sequence;
			if (sequence[sequence.length() -1] != '\n')
				cout << "\n";
			++cnt_filtered;
		}
	}

	return true;
}
//________________________________________________________________________________________

//	filter_sequences

//________________________________________________________________________________________
void filter_sequences(		std::istream&					istrm,
							std::string						regex_str,
							t_progress_indicator&			dots,
							t_collect_sequences_report_errors& errors,
							ostream&						VERBOSE)
{
	boost::regex brackets("\\(.*\\)");
	if (!boost::regex_search(regex_str, brackets))
		regex_str = "(" + regex_str + ")";

	t_filter_sequences_by_regex filter_seq
									( regex_str, dots, errors);
	BioCPP::Process_FA_Sequences_tellg(istrm, filter_seq);
	dots.finish();
	VERBOSE << "\t" << filter_seq.cnt_filtered << " sequences out of " <<
				filter_seq.cnt_sequences << " matched.\n";

}






int main(int argc, char* argv[])
{
	debug_func_cerr( 8 );

	t_program_args args;

	try
	{

		// don't use C streams
		std::ios::sync_with_stdio(false);
	
		// get command line arguments
		if (!process_command_line_options(argc, argv, args))
			return 1;
	
	
		//
		// filter sequences
		//
		BioCPP::t_collect_sequences_collect_errors collect_errors;
		args.VERBOSE() << "\tFiltering sequences from STDIN...\n";
		t_progress_indicator dots(args.VERBOSE(), 10000);
		filter_sequences(std::cin, args.regex, dots, collect_errors, args.VERBOSE());
		
		collect_errors.report(args.VERBOSE(), false);
		collect_errors.report(args.CERR(), args.detail_errors);
		args.VERBOSE() << "\tDone...\n";

	}
	catch (std::exception& e)
	{
		std_print_error(args.CERR(), e.what());
		return 1;
	}
	catch(...)
	{
		return 1;
	}

	return 0;
}
